package com.eastfair.notice.controller;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.eastfair.cache.redis.RedisOps;
import com.eastfair.config.api.ConfigServiceFeign;
import com.eastfair.config.entity.InterfaceAccount;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.enums.InterfaceAccountPlatformTypeEnum;
import com.eastfair.enums.RedisKeyEnum;
import com.eastfair.notice.dto.MsgFieldDTO;
import com.eastfair.notice.dto.MsgMqDTO;
import com.eastfair.notice.dto.MsgSendDTO;
import com.eastfair.projectcore.api.ProjectCoreServiceFeign;import com.eastfair.core.snowflake.DefaultUidGenerator;
import com.eastfair.projectcore.dto.MsgTemplatePageQuery;
import com.eastfair.projectcore.entity.MsgTemplate;
import com.eastfair.third.email.model.EmailConfig;
import com.eastfair.third.im.dto.ImConfDTO;
import com.eastfair.third.jpush.model.JPushConfig;
import com.eastfair.third.sms.model.SmsConfig;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 发送消息接口
 *
 * @ClassName MsgSendController
 * @Description 用于后端内部feign调用
 * @Author liuxiaobai
 * @Date 2021/8/5 2:48 下午
 * @Version 1.0
 */
@Slf4j
@Validated
@RestController
@RequestMapping("/msgSend")
@Api(value = "MsgSend", tags = "发送消息")
public class MsgSendController {

    final ProjectCoreServiceFeign projectCoreServiceFeign;
    final JmsTemplate jmsTemplate;
    final RedisOps redisOps;
    final ConfigServiceFeign configServiceFeign;

    @Autowired
    public MsgSendController(ProjectCoreServiceFeign projectCoreServiceFeign, JmsTemplate jmsTemplate, RedisOps redisOps
            , ConfigServiceFeign configServiceFeign) {
        this.projectCoreServiceFeign = projectCoreServiceFeign;
        this.jmsTemplate = jmsTemplate;
        this.redisOps = redisOps;
        this.configServiceFeign = configServiceFeign;
    }

    @Value("${activemq.queue.emailSend}")
    private String emailSendQueue;
    @Value("${activemq.queue.smsSend}")
    private String smsSendQueue;
    @Value("${activemq.queue.noticeSend}")
    private String noticeSendQueue;
    @Value("${activemq.queue.imSend}")
    private String imSendQueue;

    /**
     * 批量发送消息
     *
     * @param sends 消息参数list
     */
    @PostMapping("/sendBatch")
    public void sendBatch(@RequestBody List<MsgSendDTO> sends) {
        for (MsgSendDTO sendDTO : sends) {
            send(sendDTO);
        }
    }

    /**
     * 发送消息
     *
     * @param sendDTO 消息参数
     */
    @PostMapping("/send")
    public void send(@RequestBody MsgSendDTO sendDTO) {
        if (sendDTO.unCheck()) {
            return;
        }
        sendDTO.setProjectId(ContextUtil.getProjectId());
        sendDTO.setExhibitionId(ContextUtil.getExhibitionId());
        sendDTO.getSendTypes().forEach(sendType -> {
            //获取模板
            MsgTemplate template = this.projectCoreServiceFeign.getMsgTemp(MsgTemplatePageQuery.builder()
                    .projectId(sendDTO.getProjectId())
                    .msgType(sendType.getMsgTypeEnum().name())
                    .tempType(sendType.getMsgTempEnum().name())
                    .build());
            if (template == null) {
                return;
            }
            //中英文模板内容域替换
            template.setContent(replaceField(template.getContent(), sendDTO.getFields()));
            template.setEnContent(replaceField(template.getEnContent(), sendDTO.getFields()));

            switch (sendType.getMsgTypeEnum()) {
                //邮件
                case EMAIL:
                    sendEmail(sendDTO, template);
                    break;
                //短信
                case SMS:
                    sendSms(sendDTO, template);
                    break;
                //站内信
                case NOTICE:
                    sendNotice(sendDTO, template);
                    break;
                //腾讯IM
                case IM:
                    sendIm(sendDTO, template);
                    break;
                default:

            }
        });
    }

    /**
     * 发送邮件
     *
     * @param sendDTO  消息内容及配置
     * @param template 模板
     */
    private void sendEmail(MsgSendDTO sendDTO, MsgTemplate template) {
        EmailConfig emailConfig = getEmailConf(sendDTO.getProjectId(), sendDTO.getEmail(), sendDTO.getIsCode());
        if (template.getMailingId() != null) {
            emailConfig.setMailingId(template.getMailingId());
        }
        //加入邮件处理mq
        this.jmsTemplate.convertAndSend(emailSendQueue, JSON.toJSONString(MsgMqDTO.builder()
                .msgSendDTO(sendDTO)
                .emailConfig(emailConfig)
                .template(template)
                .build()));
    }

    /**
     * 发送短信
     *
     * @param sendDTO  消息内容及配置
     * @param template 模板
     */
    private void sendSms(MsgSendDTO sendDTO, MsgTemplate template) {
        String cacheKey = String.format(RedisKeyEnum.THIRD_PART_CONFIG_SMS.getKey(), sendDTO.getProjectId());
        SmsConfig smsConfig = this.redisOps.get(cacheKey);
        if (smsConfig == null) {
            R<InterfaceAccount> result = this.configServiceFeign.getByEntity(InterfaceAccount.builder().platformType(InterfaceAccountPlatformTypeEnum.THIRD_PART_SMS.name()).build());
            smsConfig = JSON.parseObject(result.getData().getAccount(), SmsConfig.class);
            this.redisOps.setEx(cacheKey, smsConfig, RedisKeyEnum.THIRD_PART_CONFIG_SMS.getSecond());
        }
        this.jmsTemplate.convertAndSend(smsSendQueue, JSON.toJSONString(MsgMqDTO.builder()
                .smsConfig(smsConfig)
                .msgSendDTO(sendDTO)
                .template(template)
                .build()));
    }

    /**
     * 发送站内信和极光
     *
     * @param sendDTO  消息内容及配置
     * @param template 模板
     */
    private void sendNotice(MsgSendDTO sendDTO, MsgTemplate template) {
        String cacheKey = String.format(RedisKeyEnum.THIRD_PART_CONFIG_JPUSH.getKey(), sendDTO.getProjectId());
        JPushConfig jPushConfig = this.redisOps.get(cacheKey);
        if (jPushConfig == null) {
            R<InterfaceAccount> account = this.configServiceFeign.getByEntity(InterfaceAccount.builder().platformType(InterfaceAccountPlatformTypeEnum.THIRD_PART_JPUSH.name()).build());
            jPushConfig = JSON.parseObject(account.getData().getAccount(), JPushConfig.class);
            this.redisOps.setEx(cacheKey, jPushConfig, RedisKeyEnum.THIRD_PART_CONFIG_JPUSH.getSecond());
        }
        this.jmsTemplate.convertAndSend(noticeSendQueue, JSON.toJSONString(MsgMqDTO.builder()
                .msgSendDTO(sendDTO)
                .jPushConfig(jPushConfig)
                .template(template)
                .build()));
    }

    /**
     * 发送im消息
     *
     * @param sendDTO  消息内容及配置
     * @param template 模板
     */
    private void sendIm(MsgSendDTO sendDTO, MsgTemplate template) {
        String cacheKey = String.format(RedisKeyEnum.THIRD_PART_CONFIG_IM.getKey(), sendDTO.getProjectId());
        ImConfDTO imConfig = this.redisOps.get(cacheKey);
        if (imConfig == null) {
            R<InterfaceAccount> account = this.configServiceFeign.getByEntity(InterfaceAccount.builder().platformType(InterfaceAccountPlatformTypeEnum.THIRD_PART_IM.name()).build());
            imConfig = JSON.parseObject(account.getData().getAccount(), ImConfDTO.class);
            this.redisOps.setEx(cacheKey, imConfig, RedisKeyEnum.THIRD_PART_CONFIG_IM.getSecond());
        }
        imConfig.setProjectId(sendDTO.getProjectId());
        this.jmsTemplate.convertAndSend(imSendQueue, JSON.toJSONString(MsgMqDTO.builder()
                .imConfig(imConfig)
                .msgSendDTO(sendDTO)
                .template(template)
                .build()));
    }

    /**
     * 替换内容
     *
     * @param content 模板文本
     * @param fields  替换域DTO
     * @return 替换后的消息内容
     */
    private String replaceField(String content, List<MsgFieldDTO> fields) {
        if (fields == null || fields.isEmpty() || StrUtil.isEmpty(content)) {
            return content;
        }
        //替换模板内所有替换域
        for (MsgFieldDTO field : fields) {
            content = content.replace(field.getFieldEnum().getField(), field.getValue());
        }

        return content;
    }

    /**
     * 邮件发送分为两种方式
     * 验证码类：配置中心数组形式配多个第三方源，轮流调用
     * 内容类：配置中心固定一个
     *
     * @param projectId 项目ID
     * @param email     接收人邮箱（注册时可能还没有ID）
     * @param isCode    是否为验证码类
     * @return 邮件配置
     */
    private EmailConfig getEmailConf(Long projectId, String email, Boolean isCode) {
        EmailConfig emailConfig;
        String cacheKey;
        if (isCode != null && isCode) {
            cacheKey = String.format(RedisKeyEnum.THIRD_PART_CONFIG_EMAIL_CODE.getKey(), projectId, email);
            emailConfig = this.redisOps.lPop(cacheKey);
            if (emailConfig == null) {
                //缓存未查到 查配置中心并加入缓存
                R<InterfaceAccount> account = this.configServiceFeign.getByEntity(InterfaceAccount.builder().platformType(InterfaceAccountPlatformTypeEnum.THIRD_PART_EMAIL.name()).build());
                List<EmailConfig> emailConfigs = JSON.parseArray(account.getData().getAccountList(), EmailConfig.class);
                //此处先pushAll再pop，保证每个conf轮流使用一次的规则
                emailConfigs.forEach(conf -> this.redisOps.rPush(cacheKey, conf));
                this.redisOps.expire(cacheKey, RedisKeyEnum.THIRD_PART_CONFIG_EMAIL_CODE.getSecond());
                emailConfig = this.redisOps.lPop(cacheKey);
            }
            //取出后再插入队尾
            this.redisOps.rPush(cacheKey, emailConfig);
        } else {
            cacheKey = String.format(RedisKeyEnum.THIRD_PART_CONFIG_EMAIL_CONTENT.getKey(), projectId);
            //先查缓存
            emailConfig = this.redisOps.get(cacheKey);
            if (emailConfig == null) {
                //缓存未查到 查配置中心并加入缓存
                R<InterfaceAccount> account = this.configServiceFeign.getByEntity(InterfaceAccount.builder().platformType(InterfaceAccountPlatformTypeEnum.THIRD_PART_EMAIL.name()).build());
                emailConfig = JSON.parseObject(account.getData().getAccount(), EmailConfig.class);
                this.redisOps.setEx(cacheKey, emailConfig, RedisKeyEnum.THIRD_PART_CONFIG_EMAIL_CONTENT.getSecond());
            }
        }

        return emailConfig;
    }

}
